; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -S -passes="loop-mssa(guard-widening)" -verify-memoryssa < %s | FileCheck %s

declare void @llvm.experimental.guard(i1,...)

@G = external global i32

; Show that we can widen into early checks within a loop, and in the process
; expose optimization oppurtunities.
define void @widen_within_loop(i1 %cond_0, i1 %cond_1, i1 %cond_2) {
; CHECK-LABEL: @widen_within_loop(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_2_GW_FR:%.*]] = freeze i1 [[COND_2:%.*]]
; CHECK-NEXT:    [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    store i32 0, ptr @G, align 4
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT:    [[WIDE_CHK1:%.*]] = and i1 [[WIDE_CHK]], [[COND_2_GW_FR]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK1]]) [ "deopt"(i32 0) ]
; CHECK-NEXT:    store i32 1, ptr @G, align 4
; CHECK-NEXT:    store i32 2, ptr @G, align 4
; CHECK-NEXT:    store i32 3, ptr @G, align 4
; CHECK-NEXT:    br label [[LOOP]]
;
entry:
  br label %loop

loop:
  store i32 0, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"(i32 0) ]
  store i32 1, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"(i32 1) ]
  store i32 2, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"(i32 2) ]
  store i32 3, ptr @G
  br label %loop
}

define void @widen_into_preheader(i1 %cond_0, i1 %cond_1, i1 %cond_2) {
; CHECK-LABEL: @widen_into_preheader(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_2_GW_FR:%.*]] = freeze i1 [[COND_2:%.*]]
; CHECK-NEXT:    [[COND_1_GW_FR:%.*]] = freeze i1 [[COND_1:%.*]]
; CHECK-NEXT:    store i32 0, ptr @G, align 4
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_1_GW_FR]]
; CHECK-NEXT:    [[WIDE_CHK1:%.*]] = and i1 [[WIDE_CHK]], [[COND_2_GW_FR]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK1]]) [ "deopt"(i32 0) ]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    store i32 1, ptr @G, align 4
; CHECK-NEXT:    store i32 2, ptr @G, align 4
; CHECK-NEXT:    store i32 3, ptr @G, align 4
; CHECK-NEXT:    br label [[LOOP]]
;
entry:
  store i32 0, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"(i32 0) ]
  br label %loop

loop:
  store i32 1, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_1) [ "deopt"(i32 1) ]
  store i32 2, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"(i32 2) ]
  store i32 3, ptr @G
  br label %loop
}

define void @dont_widen_over_common_exit(i1 %cond_0, i1 %cond_1, i1 %cond_2) {
; CHECK-LABEL: @dont_widen_over_common_exit(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    store i32 0, ptr @G, align 4
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_0:%.*]]) [ "deopt"(i32 0) ]
; CHECK-NEXT:    store i32 1, ptr @G, align 4
; CHECK-NEXT:    br i1 [[COND_1:%.*]], label [[BACKEDGE:%.*]], label [[EXIT:%.*]]
; CHECK:       backedge:
; CHECK-NEXT:    store i32 2, ptr @G, align 4
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[COND_2:%.*]]) [ "deopt"(i32 2) ]
; CHECK-NEXT:    store i32 3, ptr @G, align 4
; CHECK-NEXT:    br label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  br label %loop

loop:
  store i32 0, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"(i32 0) ]
  store i32 1, ptr @G
  br i1 %cond_1, label %backedge, label %exit

backedge:
  store i32 2, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"(i32 2) ]
  store i32 3, ptr @G
  br label %loop

exit:
  ret void
}

define void @widen_over_common_exit_to_ph(i1 %cond_0, i1 %cond_1, i1 %cond_2) {
; CHECK-LABEL: @widen_over_common_exit_to_ph(
; CHECK-NEXT:  entry:
; CHECK-NEXT:    [[COND_2_GW_FR:%.*]] = freeze i1 [[COND_2:%.*]]
; CHECK-NEXT:    store i32 0, ptr @G, align 4
; CHECK-NEXT:    [[WIDE_CHK:%.*]] = and i1 [[COND_0:%.*]], [[COND_2_GW_FR]]
; CHECK-NEXT:    call void (i1, ...) @llvm.experimental.guard(i1 [[WIDE_CHK]]) [ "deopt"(i32 0) ]
; CHECK-NEXT:    br label [[LOOP:%.*]]
; CHECK:       loop:
; CHECK-NEXT:    store i32 1, ptr @G, align 4
; CHECK-NEXT:    br i1 [[COND_1:%.*]], label [[BACKEDGE:%.*]], label [[EXIT:%.*]]
; CHECK:       backedge:
; CHECK-NEXT:    store i32 2, ptr @G, align 4
; CHECK-NEXT:    store i32 3, ptr @G, align 4
; CHECK-NEXT:    br label [[LOOP]]
; CHECK:       exit:
; CHECK-NEXT:    ret void
;
entry:
  store i32 0, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_0) [ "deopt"(i32 0) ]
  br label %loop

loop:
  store i32 1, ptr @G
  br i1 %cond_1, label %backedge, label %exit

backedge:
  store i32 2, ptr @G
  call void(i1, ...) @llvm.experimental.guard(i1 %cond_2) [ "deopt"(i32 2) ]
  store i32 3, ptr @G
  br label %loop

exit:
  ret void
}

